/*
 * 人脸验证
 * Created by RisingSun. 
 * User: MarkSpace 
 * Date: 20220315
 * Time: 11:05 
*/
layui.extend({
  setter: 'config' //配置模块
  ,faceapi: 'lib/extend/faceapi' //人脸识别
}).define(['setter','faceapi'],function(exports){ //提示：模块也可以依赖其它模块，

var setter = layui.setter;
var $ = layui.jquery;

var faceai={

	/*
     * 获取权限数据
     * 参数解释：
       url   
    */
    render:function($this, options){
       
        var divID,imgdata;
        var that=this,models,video,trackBox,canvasImg,media,sourceSize,matchedScore,mediaSize;

        this.divID = $this;
        this.imgdata = null;
        this.models = options.models ? options.models : '/static/lay/lib/models';
        this.media = options.media ? options.media : 'img';  // 输入类型，图片，视频，摄像头视频流
        this.sourceSize = options.sourcesize ? options.sourcesize : 320;  //输入的数据源大小，
        this.matchedScore = options.matchedscore ? options.matchedscore : 0.9;
        this.mediaSize  = options.mediasize ? options.mediasize : { width: 620, height:500 };
        this.imgUrl = options.imgurl ? options.imgurl : '/static/lay/lib/models/ceshi.png';
        
        that.resize();

        this.video = document.querySelector('#video');
        this.trackBox = document.querySelector('#trackBox');
        this.canvasImg = document.querySelector('#canvasImg');
        this.retryBtn = document.querySelector('#retry');
        this.compareBtn = document.querySelector('#compare');
        this.textcon = document.querySelector('#textcon');

        that.drawViewFinder();
        
        // ageGenderNet 识别性别和年龄
        // faceExpressionNet 识别表情,开心，沮丧，普通
        // faceLandmark68Net 识别脸部特征用于mobilenet算法
        // faceLandmark68TinyNet 识别脸部特征用于tiny算法
        // faceRecognitionNet 识别人脸
        // ssdMobilenetv1 google开源AI算法除库包含分类和线性回归
        // tinyFaceDetector 比Google的mobilenet更轻量级，速度更快一点
        // mtcnn  多任务CNN算法，一开浏览器就卡死
        // tinyYolov2 识别身体轮廓的算法，不知道怎么用

        Promise.all([
           // faceapi.nets.ssdMobilenetv1.loadFromUri(this.models),
           faceapi.nets.tinyFaceDetector.loadFromUri(this.models),
           faceapi.nets.faceLandmark68Net.loadFromUri(this.models),
           faceapi.nets.faceRecognitionNet.loadFromUri(this.models),
           faceapi.nets.faceExpressionNet.loadFromUri(this.models),
           faceapi.nets.ageGenderNet.loadFromUri(this.models)    
        ]).then(that.startVideo())

        that.bindEvents();

  //监听对应的人脸表情和模型数据进行比对
// video.addEventListener('play', () => {
//   const canvas = faceapi.createCanvasFromMedia(video)
//   document.body.append(canvas)
//   const displaySize = { width: video.width, height: video.height }
//   faceapi.matchDimensions(canvas, displaySize)
//   //比对好之后利用canvas进行画图
//   setInterval(async () => {
//     // const detection = await faceapi.detectSingleFace(input)  //检测图像中具有最高置信度分数的面部
//     // const detections = await faceapi.detectAllFaces(video, new faceapi.TinyFaceDetectorOptions({
//     const detections = await faceapi.detectSingleFace(video, new faceapi.TinyFaceDetectorOptions({
//           // 输入的数据源大小，这个值越小，处理速度越快。常用值：128, 160, 224, 320, 416, 512, 608
//            inputSize: 320,  // default: 416
//           // 用于过滤边界的分数阈值，大于等于这个值才被认为检测到了人脸，然后返回一个detection对象
//            scoreThreshold: 0.6  // default: 0.5
//           })).withFaceLandmarks().withFaceExpressions().withAgeAndGender(); //检测视频或者图片中全部脸
//     // console.log(detections.detection.score);
//     // if (detections.detection.score > 0.6) {
//     //     console.log(`检测到人脸，匹配度大于 0.9`);
//     //     // this.showEl(this.operationEl);
//     //     // 人脸符合要求，暂停视频流
//     //     // video.pause();
//     //     // TODO 调用后端接口进行身份验证
//     //     // return;
//     //   }
//     const resizedDetections = faceapi.resizeResults(detections, displaySize)
//     canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height)
//     faceapi.draw.drawDetections(canvas, resizedDetections);
//     faceapi.draw.drawFaceLandmarks(canvas, resizedDetections);
//     faceapi.draw.drawFaceExpressions(canvas, resizedDetections);
//     // resizedDetections.forEach(result => {
//     //   const { age, gender, genderProbability } = result;
//     //   new faceapi.draw.DrawTextField(
//     //     [
//     //       `${Math.round(age, 0)} years`,
//     //       `${gender} (${(genderProbability).toFixed(2)})`
//     //     ],
//     //     result.detection.box.bottomRight
//     //   ).draw(canvas); 
//     // });
//   }, 300)
//   })
    
    },

    // 设置相关容器
    resize:function() {
        const wraper = document.querySelector(this.divID);
        console.log(this.divID)
        // wraper.style.width = `${this.mediaSize.width}px`;
        // wraper.style.height = `${this.mediaSize.height}px`;
        wraper.setAttribute("style", 'top:50px;width:'+`${this.mediaSize.width}px`+'; height:'+ `${this.mediaSize.height}px`+';position: absolute; left:50%;right: 50%;transform: translateX(-50%);display: flex;align-items: center;justify-content: center;');
        
        const divp=document.createElement("p");
        divp.setAttribute("id", 'titlecon');
        // divp.setAttribute("class", 'layui-btn');
        divp.setAttribute("style", 'position: absolute;top:-40px; z-index:101; font-size:16px');
        divp.innerHTML='人脸识别';
        wraper.appendChild(divp);

        const divps=document.createElement("p");
        divps.setAttribute("id", 'textcon');
        divps.setAttribute("style", 'position: absolute;bottom:20px; z-index:101; font-size:20px; color:#fff;');
        divps.innerHTML='请保持脸部在取景框内';
        wraper.appendChild(divps);

        const img1=document.createElement("img");
        img1.setAttribute("id", 'img1');
        // img1.setAttribute("style", 'display:none;');
        wraper.appendChild(img1);

        const img2=document.createElement("img");
        img2.setAttribute("id", 'img2');
        // img1.setAttribute("style", 'display:none;');
        wraper.appendChild(img2);

        const video=document.createElement("VIDEO");
        video.setAttribute("width", this.mediaSize.width);
        video.setAttribute("height", this.mediaSize.height);
        video.setAttribute("autoplay", true);
        video.setAttribute("muted", true);
        video.setAttribute("style", 'z-index:10;position: absolute;');
        video.setAttribute("id", 'video');
        wraper.appendChild(video);

        const trackBox=document.createElement("canvas");
        trackBox.setAttribute("id","trackBox");
        // canvasImg.width = this.mediaSize.width;
        // canvasImg.height = this.mediaSize.height;
        trackBox.setAttribute("style", 'z-index:100;width:'+`${this.mediaSize.width}px`+'; height:'+ `${this.mediaSize.height}px`+';position: absolute; ');
        wraper.appendChild(trackBox);

        const canvasImg=document.createElement("canvas");
        canvasImg.setAttribute("id","canvasImg");
        // canvasImg.width = this.mediaSize.width;
        // canvasImg.height = this.mediaSize.height;
        canvasImg.setAttribute("style", 'z-index:99; position: absolute; left:50%;right: 50%;transform: translateX(-50%);');
        wraper.appendChild(canvasImg);

        const retry=document.createElement("button");
        retry.setAttribute("id", 'retry');
        retry.setAttribute("class", 'layui-btn');
        retry.setAttribute("style", 'position: absolute;top:100%;margin-top: 20px;visibility:hidden;margin-left:0px');
        retry.innerHTML='重新验证';
        wraper.appendChild(retry);

        const compare=document.createElement("button");
        compare.setAttribute("id", 'compare');
        compare.setAttribute("class", 'layui-btn');
        compare.setAttribute("style", 'position: absolute;top:100%;margin-top: 20px;margin-left:0px');
        compare.innerHTML='开始验证';
        wraper.appendChild(compare);

    },
    getUserMedia:function(constraints, success, error) {
            if (navigator.mediaDevices.getUserMedia) {
                //最新的标准API
                navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error);
            } else if (navigator.webkitGetUserMedia) {
                //webkit核心浏览器
                navigator.webkitGetUserMedia(constraints, success, error)
            } else if (navigator.mozGetUserMedia) {
                //firfox浏览器
                navigator.mozGetUserMedia(constraints, success, error);
            } else if (navigator.getUserMedia) {
                //旧版API
                navigator.getUserMedia(constraints, success, error);
            }
        },
    success:function(stream) {
            //兼容webkit核心浏览器
            let CompatibleURL = window.URL || window.webkitURL;
            //将视频流设置为video元素的源
            // console.log(stream);
            //播放
            video.srcObject = stream;
            video.play();
    },
    // 获取媒体流错误处理
    mediaErrorCallback:function(error) {
        const errorMap = {
           'NotSupportedError':'浏览器不支持http访问用户媒体',
           'NotAllowedError': '摄像头已被禁用，请在当前浏览器设置中开启后重试',
           'AbortError': '硬件问题，导致无法访问摄像头',
           'NotFoundError': '未检测到可用摄像头',
           'NotReadableError': '操作系统上某个硬件、浏览器或者网页层面发生错误，导致无法访问摄像头',
           'OverConstrainedError': '未检测到可用摄像头',
           'SecurityError': '摄像头已被禁用，请在系统设置或者浏览器设置中开启后重试',
           'TypeError': '类型错误，未检测到可用摄像头'
          };
        if (errorMap[error.name]) {
          layer.alert(errorMap[error.name]);
        }
    },
    //采集视频流
    startVideo:function() {
        if (navigator.mediaDevices.getUserMedia || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator
            .mozGetUserMedia) {
            //调用用户媒体设备, 访问摄像头
            this.getUserMedia({
                video: {
                    width: this.mediaSize.width,
                    height: this.mediaSize.height
                }
            }, this.success, this.mediaErrorCallback);
        } else {
            layer.alert('不支持访问用户媒体');
        }
    },
    // 人脸识别弹框的相关事件绑定
    bindEvents() {
    // "重新验证" 按钮
        this.retryBtn.onclick = () => {
          // if (this.video.paused || this.video.ended) {
            this.textcon.innerHTML='请保持脸部在取景框内';
            this.hide(this.trackBox).hide(this.retryBtn).show(this.compareBtn);
            clearInterval(this.checkSingle);
            this.video.play();
          // }
        };
        // "开始验证" 按钮
        this.compareBtn.onclick = () => {
          //显示canvas
          this.show(this.trackBox).show(this.canvasImg).show(this.retryBtn).hide(this.compareBtn);
          // 清空canvas内容；
          this.trackBox.setAttribute('height', 0);
          // this.canvasImg.setAttribute('height', 0);
          this.textcon.innerHTML='识别中···';
          // 获取当前视频流中的图像，并通过 canvas 绘制出来
          single=this.checkSingle();
          // this.canvasImg.getContext('2d').drawImage(this.video, 0, 0, this.canvasImg.width, this.canvasImg.height);
          // 将绘制的图像转化成 图片的 base64 编码
          // let image = this.canvasImg.toDataURL('image/png');
          // 百度人脸识别API要求图片不需要包括头部信息，仅base64编码即可 
          // image = image.replace('data:image/png;base64,', '');
          // console.log(image);
          // this.video.pause();
        };
    },
    hide(el) {
        el.style.visibility = 'hidden';
        return this;
    },
    show(el) {
        el.style.visibility = 'visible';
        return this;
    },

    //精确验证人脸
     checkSingle(callback) {
        
        const displaySize = { width: this.video.width, height: this.video.height };
        faceapi.matchDimensions(this.trackBox, displaySize);

        check=setInterval(async () => {
            var state=[];
                 const detections = await faceapi.detectSingleFace(video, new faceapi.TinyFaceDetectorOptions({
                 inputSize: this.sourceSize,   // 输入的数据源大小，这个值越小，处理速度越快。常用值：128, 160, 224, 320, 416, 512, 608
                 scoreThreshold: this.matchedScore // 用于过滤边界的分数阈值，大于等于这个值才被认为检测到了人脸，然后返回一个detection对象
              })).withFaceLandmarks().withFaceExpressions()/*.withAgeAndGender()*/; //检测视频或者图片中全部脸
              if(detections){
              const resizedDetections = faceapi.resizeResults(detections, displaySize);
              this.trackBox.getContext('2d').clearRect(0, 0, this.trackBox.width, this.trackBox.height);
              faceapi.draw.drawDetections(this.trackBox, resizedDetections);
              // faceapi.draw.drawFaceLandmarks(this.trackBox, resizedDetections);
              // faceapi.draw.drawFaceExpressions(this.trackBox, resizedDetections);
                    if (detections.detection.score > this.matchedScore) {
                          this.textcon.innerHTML='检测到人脸···';
                          clearInterval(check);
                          this.canvasImg.getContext('2d').drawImage(this.video, 0, 0, this.canvasImg.width, this.canvasImg.height);
                            this.hide(this.canvasImg);
                            //获取对比图表
                            document.getElementById('img1').src = this.canvasImg.toDataURL('image/png');
                            //获取特征图标
                            var canvas = document.createElement('canvas'),
                            ctx = canvas.getContext('2d'),
                            imgs = new Image();
                            imgs.src = this.imgUrl;
                            imgs.crossOrigin = 'anonymous';//解决Canvas.toDataURL 图片跨域问题
                            imgs.onload = () => {
                                // canvas.height = 300;
                                // canvas.width = 300;
                                ctx.drawImage(imgs, 0, 0, 300, 300);
                                let ext = imgs.src.substring(imgs.src.lastIndexOf(".") + 1).toLowerCase(); // 获取到图片的格式
                                document.getElementById('img2').src=canvas.toDataURL("image/" + ext);// 得到base64 编码的 dataURL
                                // canvas = null;
                            };
                            const picElem1 = document.getElementById('img1');
                            const picElem2 = document.getElementById('img2');
                            if(picElem2.src){
                               this.compareFace(picElem1,picElem2); 
                            }
                            
                          // setTimeout(this.checkMouth(), 2000);
                          // this.canvasImg.getContext('2d').drawImage(this.video, 0, 0, this.canvasImg.width, this.canvasImg.height);
                          // 人脸符合要求，暂停视频流
                          // this.video.pause();
                          // TODO 调用后端接口进行身份验证
                          // return;

                        }
                }
                
           }, 300);

    },

    //精确验证嘴巴动作
     checkMouth() {
        const displaySize = { width: this.video.width, height: this.video.height };
        faceapi.matchDimensions(this.trackBox, displaySize);
        var i=[];
        check=setInterval(async () => {
                 const detections = await faceapi.detectSingleFace(video, new faceapi.TinyFaceDetectorOptions({
                 inputSize: this.sourceSize,   // 输入的数据源大小，这个值越小，处理速度越快。常用值：128, 160, 224, 320, 416, 512, 608
                 scoreThreshold: this.matchedScore // 用于过滤边界的分数阈值，大于等于这个值才被认为检测到了人脸，然后返回一个detection对象
              })).withFaceLandmarks().withFaceDescriptor(); //检测视频或者图片中全部脸
              if(detections){
              const resizedDetections = faceapi.resizeResults(detections, displaySize);
                    if (detections.detection.score > this.matchedScore) {
                          this.textcon.innerHTML='请张张嘴···';
                          i.push(parseInt(Math.abs(detections.landmarks.relativePositions[57].y*10000-detections.landmarks.relativePositions[51].y*10000)));
                          var sum=0;
                          i.forEach((item)=>{ sum += item;});
                          if((sum/i.length)/1000>1.3){
                            clearInterval(check);
                            this.canvasImg.getContext('2d').drawImage(this.video, 0, 0, this.canvasImg.width, this.canvasImg.height);
                            this.hide(this.canvasImg);
                            //获取对比图表
                            document.getElementById('img1').src = this.canvasImg.toDataURL('image/png');
                            //获取特征图标
                            var canvas = document.createElement('canvas'),
                            img = new Image();
                            img.src = this.imgUrl;
                            img.crossOrigin = 'anonymous';//解决Canvas.toDataURL 图片跨域问题
                            img.onload = () => {
                                canvas.height = img.height;
                                canvas.width = img.width;
                                canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height);
                                let ext = img.src.substring(img.src.lastIndexOf(".") + 1).toLowerCase(); // 获取到图片的格式
                                let dataURL = canvas.toDataURL("image/" + ext); // 得到base64 编码的 dataURL
                                document.getElementById('img2').src=dataURL;
                                canvas = null;
                            };
                            const picElem1 = document.getElementById('img1');
                            const picElem2 = document.getElementById('img2');
                            this.compareFace(picElem1,picElem2);
                            this.video.pause();
                            return;
                          }
                        }
                }
           }, 300)

    },
    //网络图片
    
    // convertImgToBase64(url) {
    //     // var data={};
    //     let canvas = document.createElement('canvas'),
    //     ctx = canvas.getContext('2d'),
    //     img = new Image();
    //     img.src = url;
    //     img.crossOrigin = 'anonymous';//解决Canvas.toDataURL 图片跨域问题
    //     img.onload = () => {
    //       canvas.height = img.height;
    //       canvas.width = img.width;
    //       ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
    //       let ext = img.src.substring(img.src.lastIndexOf(".") + 1).toLowerCase(); // 获取到图片的格式
    //       let dataURL = canvas.toDataURL("image/" + ext); // 得到base64 编码的 dataURL
    //       // console.log(dataURL);
    //       // callback && callback(dataURL);
    //       this.imgdata=dataURL;
    //       canvas = null;
    //     };
    //     // return data;
        
    // },
    // 比较人脸
    compareFace:function(picElem1,picElem2){

        console.log(picElem1);
        console.log(picElem2);
        const input = faceapi.fetchImage(this.imgUrl);
        const picture1 = faceapi.computeFaceDescriptor(picElem1);
        const picture2 = faceapi.computeFaceDescriptor(input);
        console.log(picture1);
        console.log(picture2);

         const distance = faceapi.utils.round(
         faceapi.euclideanDistance(picture1, picture2)
         )
        
        console.log(distance);
        if (distance > 0.6) {
           console.log('验证失败');
        }else{
            console.log('成功');
        }
    },

    //验证全部人脸
     checkAll() {

        const displaySize = { width: this.video.width, height: this.video.height };
        faceapi.matchDimensions(this.trackBox, displaySize);
        check=setInterval(async () => {
                 const detections = await faceapi.detectAllFaces(video, new faceapi.TinyFaceDetectorOptions({
                 inputSize: this.sourceSize,   // 输入的数据源大小，这个值越小，处理速度越快。常用值：128, 160, 224, 320, 416, 512, 608
                 scoreThreshold: this.matchedScore // 用于过滤边界的分数阈值，大于等于这个值才被认为检测到了人脸，然后返回一个detection对象
              })).withFaceLandmarks().withFaceExpressions().withAgeAndGender(); //检测视频或者图片中全部脸

              const resizedDetections = faceapi.resizeResults(detections, displaySize);
              this.trackBox.getContext('2d').clearRect(0, 0, this.trackBox.width, this.trackBox.height);
              faceapi.draw.drawDetections(this.trackBox, resizedDetections);
              faceapi.draw.drawFaceLandmarks(this.trackBox, resizedDetections);
              faceapi.draw.drawFaceExpressions(this.trackBox, resizedDetections);
           }, 300)

    },
    
    /** @name 绘制取景框 */
    drawViewFinder() {    
    const context = this.canvasImg.getContext("2d");
    if (!context) return;
    this.canvasImg.setAttribute('width', this.mediaSize.width - 120);
    this.canvasImg.setAttribute('height', this.mediaSize.height - 120);
      // topleft
      context.beginPath();//开启当前的绘制路径
      context.moveTo(0, 0);
      context.lineTo(50,0);
      context.moveTo(0,0);
      context.lineTo(0,50);
      context.lineWidth = 4;
      context.strokeStyle = "#fff";
      context.stroke();//沿着坐标点的路径顺序绘制直线  
      context.closePath();   //关闭当前的绘制路径

      // topright
      context.beginPath();//开启当前的绘制路径
      context.moveTo(this.canvasImg.width,0);
      context.lineTo(this.canvasImg.width-50,0);
      context.moveTo(this.canvasImg.width, 0);
      context.lineTo(this.canvasImg.width,50);
      context.lineWidth = 4;
      context.strokeStyle = "#fff"; 
      context.stroke();//沿着坐标点的路径顺序绘制直线
      context.closePath();   //关闭当前的绘制路径

      // bottomleft
      context.beginPath();//开启当前的绘制路径
      context.moveTo(0,this.canvasImg.height);
      context.lineTo(0,this.canvasImg.height-50);
      context.moveTo(0,this.canvasImg.height);
      context.lineTo(50,this.canvasImg.height);
      context.lineWidth = 4;
      context.strokeStyle = "#fff"; 
      context.stroke();//沿着坐标点的路径顺序绘制直线
      context.closePath();   //关闭当前的绘制路径

      // bottomright
      context.beginPath();//开启当前的绘制路径
      context.moveTo(this.canvasImg.width,this.canvasImg.height);
      context.lineTo(this.canvasImg.width,this.canvasImg.height-50);
      context.moveTo(this.canvasImg.width,this.canvasImg.height);
      context.lineTo(this.canvasImg.width-50,this.canvasImg.height);
      context.lineWidth = 4;
      context.strokeStyle = "#fff"; 
      context.stroke();//沿着坐标点的路径顺序绘制直线
      context.closePath();   //关闭当前的绘制路径

    }

}  
exports('faceai', faceai);
});
